import torch
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd📘 Note Format Guide
This format serves as a structured guide for organizing lecture content, personal interpretation, experiments, and study-related questions.
| Type | What It Means | When I Use It |
|---|---|---|
| 📝 Lecture | Original material from the professor’s notes | When I’m referencing core concepts or provided code |
| 🗣️ In-Class Note | Verbal explanations shared during the lecture | When I want to record something the professor said in class but didn’t include in the official notes |
| ✍️ My Note | My thoughts, interpretations, or additional explanations | When I reflect on or explain something in my own words |
| 🔬 Experiment | Code I tried out or changed to explore further | When I test variations or go beyond the original example |
| ❓ Question | Questions I had while studying | When I want to revisit or research something more deeply |
📝 🗣️ ✍️ 🔬 ❓
1. 강의노트 원본 및 영상 링크
2. Imports 📝
plt.rcParams['figure.figsize'] = (4.5, 3.0)3. 로지스틱 – sig(linr(x)) 📝
A. 로지스틱 모형
- \(x\)가 커질수록 (혹은 작아질수록) \(y=1\)이 잘나오는 모형은 아래와 같이 설계할 수 있음 <— 외우세요!!!
\(y_i \sim {\cal B}(\pi_i),\quad\) where \(\pi_i = \frac{\exp(w_0+w_1x_i)}{1+\exp(w_0+w_1x_i)} = \frac{1}{1+\exp(-w_0-w_1x_i)}\)
\(\hat{y}_i= \frac{\exp(\hat{w}_0+\hat{w}_1x_i)}{1+\exp(\hat{w}_0+\hat{w}_1x_i)}=\frac{1}{1+\exp(-\hat{w}_0-\hat{w}_1x_i)}\)
- 회귀모형과 로지스틱 모형의 비교
- 회귀모형: \(y_i \sim {\cal N}(w_0+w_1x_i, \sigma^2)\)1
- 로지스틱: \(y_i \sim {\cal B}\big(\frac{\exp(w_0+w_1x_i)}{1+\exp(w_0+w_1x_i)}\big)\)
- 우리가 예측하고 싶은것
- 회귀모형: 정규분포의 평균을 예측하고 싶음. 즉 \(w_0+w_1x_i\)를 예측하고 싶음. 예측값으로는 \(\hat{w}_0 + \hat{w}_1x_i\)를 사용!
- 로지스틱: 베르누이의 평균을 예측하고 싶음. 즉 \(\frac{\exp(w_0+w_1x_i)}{1+\exp(w_0+w_1x_i)}\)를 예측하고 싶음. 예측값으로는 \(\frac{\exp(\hat{w}_0+\hat{w}_1x_i)}{1+\exp(\hat{w}_0+\hat{w}_1x_i)}\)를 사용!
B. 데이터 – 스펙과 취업
🗣️(
- 데이터 만들기
torch.linspace(-1,1,2000)tensor([-1.0000, -0.9990, -0.9980, ..., 0.9980, 0.9990, 1.0000])
len(torch.linspace(-1,1,2000))2000
x = torch.linspace(-1,1,2000).reshape(2000,1)
xtensor([[-1.0000],
[-0.9990],
[-0.9980],
...,
[ 0.9980],
[ 0.9990],
[ 1.0000]])
- 이 상태에서 선형 변환을 한다면
- -1 + x*5 : 선형 모델
- 로지스틱 모형은
x = torch.linspace(-1,1,2000).reshape(2000,1)
prob = torch.exp(-1 + x*5) / (1+ torch.exp(-1 + x*5))
plt.plot(x,prob)
- 다른 방법 (보기 좋게)
x = torch.linspace(-1,1,2000).reshape(2000,1)
w0, w1 = -1, 5
prob = torch.exp(w0 + x*w1) / (1+ torch.exp(w0 + x*w1))
plt.plot(x,prob)
probtensor([[0.0025],
[0.0025],
[0.0025],
...,
[0.9818],
[0.9819],
[0.9820]])
prob.shapetorch.Size([2000, 1])
- y 만들기 (prob로 베르누이 시행)
torch.bernoulli(prob)tensor([[0.],
[0.],
[0.],
...,
[1.],
[1.],
[1.]])
torch.bernoulli(prob).shapetorch.Size([2000, 1])
- seed 고정 후 시각화
torch.manual_seed(43052)
x = torch.linspace(-1,1,2000).reshape(2000,1)
w0, w1 = -1, 5
prob = torch.exp(w0 + x*w1) / (1+ torch.exp(w0 + x*w1))
y = torch.bernoulli(prob)plt.plot(x,y) # 보기 쉽지는 않음 
plt.plot(x,y, 'o') # 점들이 너무 많이 겹침
plt.plot(x,y,'.',alpha=0.03) # 투명도 조절
- x가 증가할수록 y는 1이 나올 가능성이 높아지고
- x가 감소할수록 y는 0이 나올 가능성이 높아짐
plt.plot(x,y,'.',alpha=0.03) # 관측(error 포함)
plt.plot(x,prob,'--') # 실체 데이터에서는 관측 불가능 (error-free structure)
)🗣️
torch.manual_seed(43052)
x = torch.linspace(-1,1,2000).reshape(2000,1)
w0,w1 = -1, 5
prob = torch.exp(w0+w1*x) / (1+torch.exp(w0+w1*x))
y = torch.bernoulli(prob)plt.plot(x,y,'.',alpha=0.03)
plt.plot(x[0],y[0],'.',label=r"$(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--r',label=r"prob (true, unknown) = $\frac{exp(-1+5x)}{1+exp(-1+5x)}$")
plt.legend()
- 🗣️
- prob: 확률
- 파란색 점: 관측값
- 목표: 빨간색 선 잘 맞추기
- 방법: 최초의 곡선을 그리고 update
C. Step1: net 설계 (모델링)
- 최초의 곡선을 그려보자.
- 최초의직선: \(\hat{y}_i= \hat{w}_0+\hat{w}_1x_i\) 에서 아무 \(\hat{w}_0\), \(\hat{w}_1\) 을 설정하면 된다.
- 최초의곡선: \(\hat{y}_i= \frac{\exp(\hat{w}_0+\hat{w}_1x_i)}{1+\exp(\hat{w}_0+\hat{w}_1x_i)}=\frac{1}{1+\exp(-\hat{w}_0-\hat{w}_1x_i)}\) 에서 아무 \(\hat{w}_0\), \(\hat{w}_1\) 을 설정하면 된다.
일단은 초기 설정값을 \(\hat{w}_0 = -0.8\), \(\hat{w}_1 = -0.3\) 으로 하자. (실제값은 \(w_0=-1\), \(w_1=5\) 이다)
# 방법1 – l1, sigmoid
🗣️(
w0hat = -4
w1hat = 10
yhat = torch.exp(w0hat + w1hat*x) / (1+ torch.exp(w0hat + w1hat*x))yhattensor([[8.3153e-07],
[8.3989e-07],
[8.4833e-07],
...,
[9.9748e-01],
[9.9750e-01],
[9.9753e-01]])
plt.plot(x,y,'.',alpha=0.03)
plt.plot(x, yhat, '--')
- yhat을 다음과 같이 할 수도 있음
linr = torch.nn.Linear(1,1)
# linr(x)def sigmoid(x):
return torch.exp(x) / (1+ torch.exp(x)) # 편의상 linr(x) 대신 x로 작성linr(x)tensor([[ 0.6311],
[ 0.6304],
[ 0.6297],
...,
[-0.6902],
[-0.6909],
[-0.6916]], grad_fn=<AddmmBackward0>)
yhat = sigmoid(linr(x))
plt.plot(x, yhat.data) # 곡선 중 일부만 그려져 직선처럼 보임
- linr(x)가 계산되는 과정
linr.weight, linr.bias(Parameter containing:
tensor([[-0.6613]], requires_grad=True),
Parameter containing:
tensor([-0.0303], requires_grad=True))
-0.6613*x + -0.0303tensor([[ 0.6310],
[ 0.6303],
[ 0.6297],
...,
[-0.6903],
[-0.6909],
[-0.6916]])
- 값을 아까처럼 지정해주면
linr.weight.data = torch.tensor([[10.0]])
linr.bias.data = torch.tensor([-4.0])❓ bias는 [[-4.0]]이 아니라 [-4.0]
🔬(
linr.weight.data = torch.tensor([[10.0]])
linr.bias.data = torch.tensor([[-4.0]])linr(x)tensor([[-14.0000],
[-13.9900],
[-13.9800],
...,
[ 5.9800],
[ 5.9900],
[ 6.0000]], grad_fn=<AddmmBackward0>)
❓ 상관없는듯?
linr.weight.data = torch.tensor([10.0])
linr.bias.data = torch.tensor([[-4.0]])# linr(x) # error: RuntimeError: mat2 must be a matrix, got 1-D tensor)🔬
🔬 참고) -4.0이 아니라 -4를 쓰면 error
linr.weight.data = torch.tensor([[10.0]])
linr.bias.data = torch.tensor([-4.0])linr(x)tensor([[-14.0000],
[-13.9900],
[-13.9800],
...,
[ 5.9800],
[ 5.9900],
[ 6.0000]], grad_fn=<AddmmBackward0>)
w0hat + w1hat*x # 위와 동일tensor([[-14.0000],
[-13.9900],
[-13.9800],
...,
[ 5.9800],
[ 5.9900],
[ 6.0000]])
-4*x + 10 # 이것도 동일tensor([[14.0000],
[13.9960],
[13.9920],
...,
[ 6.0080],
[ 6.0040],
[ 6.0000]])
- 다시 정리하면
def sigmoid(x):
return torch.exp(x) / (1+ torch.exp(x)) # 편의상 linr(x) 대신 x로 작성l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[10.0]])
l1.bias.data = torch.tensor([-4.0])
#yhat = torch.exp(l1(x)) / (1+ torch.exp(l1(x)))
yhat = sigmoid(l1(x))plt.plot(x,yhat.data)
- 값을 바꾸고 싶으면
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
#yhat = torch.exp(l1(x)) / (1+ torch.exp(l1(x)))
yhat = sigmoid(l1(x))plt.plot(x,yhat.data)
plt.plot(x,y,'.',alpha=0.03)
plt.plot(x, yhat.data, '--')
)🗣️
l1 = torch.nn.Linear(1,1)
l1(x) # w0hat + w1hat*x tensor([[ 0.4735],
[ 0.4728],
[ 0.4721],
...,
[-0.9890],
[-0.9897],
[-0.9905]], grad_fn=<AddmmBackward0>)
🗣️ 실행할 때마다 달라지므로 아래와 같이 고정
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])def sigmoid(x):
return torch.exp(x)/(1+torch.exp(x))plt.plot(x,y,'.',alpha=0.03)
plt.plot(x[0],y[0],'o',label=r"$(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--r',label=r"prob (true, unknown) = $\frac{exp(-1+5x)}{1+exp(-1+5x)}$")
plt.plot(x,sigmoid(l1(x)).data,'--b', label=r"prob (estimated) = $(x_i,\hat{y}_i)$ -- first curve")
plt.legend()
#
# 방법2 – l1, a1
🗣️(
x -> w0hat + w1hat*x # 최초의 곡선을 그리기 위한 선형 변환
u = w0hat + w1hat*x # 결과를 u로 저장
first_curve = yhat = prob_hat = sigmoid(u)
u = w0hat + w1hat*x = l1(x) # l1을 만든다면 이렇게도 쓸 수 있음
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
u = l1(x)
yhat = sigmoid(u)- sigmoid는 직접 만들었음
sigmoid?Signature: sigmoid(x) Docstring: <no docstring> File: /tmp/ipykernel_30452/3273882758.py Type: function
sigmoid??Signature: sigmoid(x) Docstring: <no docstring> Source: def sigmoid(x): return torch.exp(x)/(1+torch.exp(x)) File: /tmp/ipykernel_30452/3273882758.py Type: function
- 다음과 같이도 할 수 있음 (torch.nn의 클래스 이용)
sig = torch.nn.Sigmoid()
sigSigmoid()
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
u = l1(x)
yhat = sig(u)yhattensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
yhat = sig(l1(x)) # x --> l1 --> sig 로 이해plt.plot(x,y,'.',alpha=0.03)
plt.plot(x[0],y[0],'o',label=r"$(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--r',label=r"prob (true, unknown) = $\frac{exp(-1+5x)}{1+exp(-1+5x)}$")
plt.plot(x,sigmoid(l1(x)).data,'--b', label=r"prob (estimated) = $(x_i,\hat{y}_i)$ -- first curve")
plt.legend()
- 방법1과 동일한 결과
)🗣️
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])a1 = torch.nn.Sigmoid()sigmoid(l1(x)), a1(l1(x)) # 똑같아요(tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<DivBackward0>),
tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>))
- 지금까지의 구현 확인
plt.plot(x,y,'.',alpha=0.03)
plt.plot(x[0],y[0],'o',label=r"$(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--r',label=r"prob (true, unknown) = $\frac{exp(-1+5x)}{1+exp(-1+5x)}$")
plt.plot(x,a1(l1(x)).data,'--b', label=r"prob (estimated) = $(x_i,\hat{y}_i)$ -- first curve with $(a_1 \circ l_1)(x)$")
plt.legend()
#
# 방법3 - l1, a1 만들고 \(\to\) net
🗣️(
yhattensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
a1(l1(x))tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
- net = al \(\circ\) l1 을 정의하여 net(x)도 같은 결과를 나오게 하고 싶음
torch.nn.Sequential(l1,a1)Sequential(
(0): Linear(in_features=1, out_features=1, bias=True)
(1): Sigmoid()
)
net = torch.nn.Sequential(l1,a1)
net(x) # a1(l1(x))tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
- 이렇게 한 이유: parameters()를 이용하여 optimizer를 만들 수 있음
net.parameters()<generator object Module.parameters at 0x7f34682a17b0>
)🗣️
- 관찰: 지금 아래의 구조이다.
\[{\bf x} \overset{l_1}{\to} {\bf u} \overset{a_1}{\to} {\bf v} = \hat{\bf y}\]
- 소망: 함수 \(l_1, a_1\) 의 합성을 하나로 묶어서
\[(a_1\circ l_1)({\bf x}) := net({\bf x})\]
이러한 기능을 하는 하나의 함수 \(net\)을 만들 수 없을까?
l1 = torch.nn.Linear(1,1)
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
a1 = torch.nn.Sigmoid()net = torch.nn.Sequential(l1,a1) #l1을 취하고 그다음에 a1을 취하라는 의미net(x), a1(l1(x)), sigmoid(l1(x))(tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>),
tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>),
tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<DivBackward0>))
* net 구조 잠깐 살펴보기
🗣️(
netSequential(
(0): Linear(in_features=1, out_features=1, bias=True)
(1): Sigmoid()
)
net[0]Linear(in_features=1, out_features=1, bias=True)
l1Linear(in_features=1, out_features=1, bias=True)
net[1]Sigmoid()
aaa = torch.nn.Sigmoid()
aaaSigmoid()
- net가 리스트처럼 되어 있어 첫번째 원소 net[0]은 l1 이고 두번째 원소 net[1]은 aaa인듯
- 확인 방법: 아래
l1 is net[0]True
a1 is net[1]True
- 다른 확인 방법
- 오브젝트: 메모리에 저장
- 저장되어 있는 주소가 동일하면 같은 오브젝트
id(net[0]), id(l1)(139863062109248, 139863062109248)
id(net[1]), id(a1)(139863062108960, 139863062108960)
net(x), a1(l1(x))(tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>),
tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>))
net(x), net[1](net[0](x)) # 이것도 동일(tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>),
tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>))
)🗣️
net[0], net[1](Linear(in_features=1, out_features=1, bias=True), Sigmoid())
l1 is net[0]True
a1 is net[1]True
#
# 방법4 – net을 바로 만들기
🗣️(
# x --> yhat: 회귀분석에서 최초의 직선 바로 만드는 방법
net = torch.nn.Linear(1,1)
yhat - net(x)# x --> yhat: 로지스틱에서 최초의 곡선 바로 만드는 방법
net = torch.nn.Sequential(
l1,
a1
)
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
yhat = net(x)net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
yhat = net(x)net[0].weight # 아무 parameter가 들어가 있음Parameter containing:
tensor([[0.4945]], requires_grad=True)
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.3]])
net[0].bias.data = torch.tensor([-0.8])
yhat = net(x)net(x)tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
)🗣️
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.3]])
net[0].bias.data = torch.tensor([-0.8])
yhat = net(x)net(x)tensor([[0.3775],
[0.3775],
[0.3774],
...,
[0.2499],
[0.2498],
[0.2497]], grad_fn=<SigmoidBackward0>)
🗣️ 결론: 위의 방법으로 사용하면 됨
#
D. Step1~4
🗣️(
- 학습 시작
plt.plot(x,y,'.',alpha=0.03) # given data
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.3]])
net[0].bias.data = torch.tensor([-0.8])
yhat = net(x)plt.plot(x,y,'.',alpha=0.03) # given data
plt.plot(x,net(x).data, '--') # 최초의 곡선 그리기
- 최초의 곡선보다 나은 곡선을 찾으며 update하면 됨
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.3]])
net[0].bias.data = torch.tensor([-0.8])
yhat = net(x)
loss = torch.mean((y-yhat)**2) # loss 함수를 만들어 줌
losstensor(0.2747, grad_fn=<MeanBackward0>)
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.3]])
net[0].bias.data = torch.tensor([-0.8])
optimizr = torch.optim.SGD(net.parameters(), lr=0.25)
for epoc in range(200):
yhat = net(x)
loss = torch.mean((y-yhat)**2)
loss.backward() # 미분
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.03)
plt.plot(x,net(x).data, '--')
- 최초의 곡선보다는 그럴듯해짐
- 알고 있는 True 값과 비교해보면
- 주황색 선: True
- 200번 정도 반복하니 어느 정도 온 것 같지만 딱 맞다고 보기는 어려움
plt.plot(x,y,'.',alpha=0.03)
plt.plot(x,prob,'--')
plt.plot(x,net(x).data, '--')
- 200번 더 (총 400번)
for epoc in range(200):
yhat = net(x)
loss = torch.mean((y-yhat)**2)
loss.backward() # 미분
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.03)
plt.plot(x,prob,'--')
plt.plot(x,net(x).data, '--')
- 200번 더 (총 600번)
for epoc in range(200):
yhat = net(x)
loss = torch.mean((y-yhat)**2)
loss.backward() # 미분
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.03)
plt.plot(x,prob,'--')
plt.plot(x,net(x).data, '--')
- 200번 더 (총 800번)
for epoc in range(200):
yhat = net(x)
loss = torch.mean((y-yhat)**2)
loss.backward() # 미분
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.03)
plt.plot(x,prob,'--')
plt.plot(x,net(x).data, '--')
- 돌릴수록 가까워질 것 같음
)🗣️
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1, out_features=1),
torch.nn.Sigmoid()
)
l1, a1 = net
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
for epoc in range(100):
## 1
yhat = net(x)
## 2
loss = torch.mean((y-yhat)**2)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 100 epochs')Text(0.5, 1.0, 'after 100 epochs')

for epoc in range(4900):
## 1
yhat = net(x)
## 2
loss = torch.mean((y-yhat)**2)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 5000 epochs')Text(0.5, 1.0, 'after 5000 epochs')

🗣️ 로지스틱이 해결된 것처럼 보임
🗣️(
- 다음과 같이 해도 마찬가지 (초기값은 크게 중요하지 않으므로)
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1, out_features=1),
torch.nn.Sigmoid()
)
# l1, a1 = net
# l1.weight.data = torch.tensor([[-0.3]])
# l1.bias.data = torch.tensor([-0.8])
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
for epoc in range(100):
## 1
yhat = net(x)
## 2
loss = torch.mean((y-yhat)**2)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 100 epochs')Text(0.5, 1.0, 'after 100 epochs')

for epoc in range(4900):
## 1
yhat = net(x)
## 2
loss = torch.mean((y-yhat)**2)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 5000 epochs')Text(0.5, 1.0, 'after 5000 epochs')

- 성공한 것 같지만 실상은 그렇지 않음
for epoc in range(4900):
## 1
yhat = net(x)
## 2
loss = torch.mean((y-yhat)**2) # 이 부분에 문제가 있어 설명할 예정
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad())🗣️
4. 학습과정 시각화 및 문제인식 📝
A. 시각화를 위한 준비
def plot_loss(loss_fn, ax=None, Wstar=[-1,5]):
w0hat,w1hat =torch.meshgrid(torch.arange(-10,3,0.1),torch.arange(-1,10,0.1),indexing='ij')
w0hat = w0hat.reshape(-1)
w1hat = w1hat.reshape(-1)
def l(w0hat,w1hat):
yhat = torch.exp(w0hat+w1hat*x)/(1+torch.exp(w0hat+w1hat*x))
return loss_fn(yhat,y)
loss = list(map(l,w0hat,w1hat))
#---#
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(1,1,1,projection='3d')
ax.scatter(w0hat,w1hat,loss,s=0.001)
ax.scatter(w0hat[::20],w1hat[::20],loss[::20],s=0.1,color='C0')
w0star,w1star = np.array(Wstar).reshape(-1)
ax.scatter(w0star,w1star,l(w0star,w1star),s=200,marker='*',color='red',label=f"W=[{w0star:.1f},{w1star:.1f}]")
#---#
ax.elev = 15
ax.dist = -20
ax.azim = 75
ax.legend()
ax.set_xlabel(r'$w_0$') # x축 레이블 설정
ax.set_ylabel(r'$w_1$') # y축 레이블 설정
ax.set_xticks([-10,-5,0]) # x축 틱 간격 설정
ax.set_yticks([-10,0,10]) # y축 틱 간격 설정def _learn_and_record(net, loss_fn, optimizr):
yhat_history = []
loss_history = []
What_history = []
Whatgrad_history = []
What_history.append([net[0].bias.data.item(), net[0].weight.data.item()])
for epoc in range(100):
## step1
yhat = net(x)
## step2
loss = loss_fn(yhat,y)
## step3
loss.backward()
## step4
optimizr.step()
## record
if epoc % 5 ==0:
yhat_history.append(yhat.reshape(-1).data.tolist())
loss_history.append(loss.item())
What_history.append([net[0].bias.data.item(), net[0].weight.data.item()])
Whatgrad_history.append([net[0].bias.grad.item(), net[0].weight.grad.item()])
optimizr.zero_grad()
return yhat_history, loss_history, What_history, Whatgrad_history
def show_animation(net, loss_fn, optimizr):
yhat_history,loss_history,What_history,Whatgrad_history = _learn_and_record(net,loss_fn,optimizr)
fig = plt.figure(figsize=(7.5,3.5))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
## ax1: 왼쪽그림
ax1.scatter(x,y,alpha=0.01)
ax1.scatter(x[0],y[0],color='C0',label=r"observed data = $(x_i,y_i)$")
ax1.plot(x,prob,'--',label=r"prob (true) = $(x_i,\frac{exp(-1+5x_i)}{1+exp(-1+5x_i)})$")
line, = ax1.plot(x,yhat_history[0],'--',label=r"prob (estimated) = $(x_i,\hat{y}_i)$")
ax1.legend()
## ax2: 오른쪽그림
plot_loss(loss_fn,ax2)
ax2.scatter(np.array(What_history)[0,0],np.array(What_history)[0,1],loss_history[0],color='blue',s=200,marker='*')
def animate(epoc):
line.set_ydata(yhat_history[epoc])
w0hat = np.array(What_history)[epoc,0]
w1hat = np.array(What_history)[epoc,1]
w0hatgrad = np.array(Whatgrad_history)[epoc,0]
w1hatgrad = np.array(Whatgrad_history)[epoc,1]
ax2.scatter(w0hat,w1hat,loss_history[epoc],color='grey')
ax2.set_title(f"What.grad=[{w0hatgrad:.4f},{w1hatgrad:.4f}]",y=0.8)
fig.suptitle(f"epoch={epoc*5} // What=[{w0hat:.2f},{w1hat:.2f}] // Loss={loss_fn.__class__.__name__} // Opt={optimizr.__class__.__name__}")
return line
ani = animation.FuncAnimation(fig, animate, frames=20)
plt.close()
return anifrom matplotlib import animation
plt.rcParams["animation.html"] = "jshtml"함수사용법
loss_fn = torch.nn.MSELoss()
plot_loss(loss_fn)
🗣️(
def loss_fn2(yhat,y):
return loss_fn(yhat,y)*2plot_loss(loss_fn2)
- z축만 2배 증가 (함수: 곡면을 그려주는 역할)
# show_animation??- Signature: show_animation(net, loss_fn, optimizr)
- net: 초기 설정 값 (w0, w1)
- loss_fn: 그림
- optimizr: 학습 과정
- 밑 코드: 어떠한 초기값을 받아 학습하는 과정을 그려줌
- 실행할 때마다 초기값 달라짐
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
show_animation(net,loss_fn,optimizr)- 초기값 고정
- 만약 학습률이 2.5면 더 빨리 떨어짐
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].weight.data = torch.tensor([[-0.8]])
net[0].bias.data = torch.tensor([-0.3])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
show_animation(net,loss_fn,optimizr))🗣️
torch.manual_seed(42)
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
show_animation(net,loss_fn,optimizr)- 🗣️
- 초기값에 따라 학습이 달라짐
- 만약 초기 값이 우측 상단이라면 평평하기 때문에 update가 아주 조금씩 일어남
B. 좋은 초기값
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-0.8])
net[0].weight.data = torch.tensor([[-0.3]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- 🗣️
- 이 경우는 기다리면 학습이 잘 될 거 같음
C. 가능성 있는 초기값
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-3.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- 🗣️
- 마지막에 약간 희망이 보임
- 마음 먹고 20,000번 정도 돌리면 될 거 같음
D. 최악의 초기값
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-10.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- 🗣️
- 이 경우는 희망이 없음
- 곡선이 아래로 볼록한 2차 함수가 아니고 4차 함수라면 학습률과 초기 값에 따라 갖혀버릴 수도 있고 운에 따라 달라짐
해결하는 접근법:
컴공스타일: 에폭을 늘려볼까?
산공스타일: 옵티마이저를 바꿔볼까?
통계스타일: Loss를 바꿔볼까?
🗣️
- 초기 값을 바꿔가며 무수히 실행하며 찾음
- 이 어려운 곡면에 대해 옵티마이저를 수정
- 곡면 자체를 최적화가 잘 되게 바꿈 (loss 함수를 바꿈: MSE Loss 말고 다른 Loss?)
5. 손실함수의 개선 📝
A. BCE Loss를 사용하여 학습
- BCE loss라는게 있음.
- \(loss= - \sum_{i=1}^{n} \big(y_i\log(\hat{y}_i)+(1-y_i)\log(1-\hat{y}_i)\big)\)
- https://en.wikipedia.org/wiki/Cross-entropy
🗣️(
yi = 0
yi_hat = 0.001
log(1) = 0
loss = 0
yi = 0
yi_hat = 0.9999
log(1-0.999) = log(0) = -무한대
loss = 무한대
yi = 1
yi_hat = 1
loss = 0
yi = 1
yi_hat = 0.0001
loss = 무한대
- 비슷할수록 0, 다를수록 무한대까지 감 -> loss의 역할은 함
- 원리: - log likelihoood
)🗣️
- 🗣️
- net[0] = torch.nn.Linear(in_features=1, out_features=1)
- net[1] = torch.nn.Sigmoid()
- net = [net[0], net[1]] 느낌
- l1, a1 = [net[0], net[1]] 느낌
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1, out_features=1),
torch.nn.Sigmoid()
)
l1, a1 = net
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
for epoc in range(100):
## 1
yhat = net(x)
## 2
#loss = torch.mean((y-yhat)**2) # loss_fn(yhat,y)
loss = -torch.mean(y*torch.log(yhat) + (1-y)*torch.log(1-yhat))
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 100 epochs')Text(0.5, 1.0, 'after 100 epochs')

같은 100 에폭인데 훨씬 잘맞춤..
🗣️ 동일한 초기 값
- loss수식을 못외우겠다면?
net = torch.nn.Sequential(
torch.nn.Linear(in_features=1, out_features=1),
torch.nn.Sigmoid()
)
l1, a1 = net
l1.weight.data = torch.tensor([[-0.3]])
l1.bias.data = torch.tensor([-0.8])
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
for epoc in range(100):
## 1
yhat = net(x)
## 2
loss = loss_fn(yhat,y) # yhat부터 써야함
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'.',alpha=0.05)
plt.plot(x,prob,'--r')
plt.plot(x,yhat.data,'--b')
plt.title('after 100 epochs')Text(0.5, 1.0, 'after 100 epochs')

B. Loss Function 시각화
plot_loss(torch.nn.MSELoss())
🗣️ MSELoss는 우측 상단에 있으면 안 될 것 같음
plot_loss(torch.nn.BCELoss())
- 비교해보자.
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1,projection='3d')
ax2 = fig.add_subplot(1,2,2,projection='3d')
plot_loss(torch.nn.MSELoss(),ax1)
plot_loss(torch.nn.BCELoss(),ax2)
- 🗣️
- 오른쪽과 같은 경우를 어려운 말로 convex function이라고 함
- loss 함수가 convex function이면 수렴시키기 쉬움
C. 학습과정 시각화 – 좋은 초기값
- MSELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-0.8])
net[0].weight.data = torch.tensor([[-0.3]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- BCELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-0.8])
net[0].weight.data = torch.tensor([[-0.3]])
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ 같은 초기값인데 BCELoss가 더 수렴을 잘 할 것 같음
D. 학습과정 시각화 – 가능성 있는 초기값
- MSELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-3.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- BCELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-3.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ BCELoss는 처음부터 잘 떨어짐
E. 학습과정 시각화 – 최악의 초기값
- MSELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-10.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- BCELoss
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-10.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ BCELoss는 처음부터 잘 떨어짐
6. 옵티마이저의 개선 📝
- 🗣️
- Loss 함수를 항상 convex하게 만들 수 있는 것은 아님
- 때로는 optimizer를 잘 만들어서 해결할 수도 있어야 함
- SGD: Stochastic Gradient Descent
A. 학습과정 시각화 – 좋은 초기값
- MSELoss + SGD
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-0.8470])
net[0].weight.data = torch.tensor([[-0.3467]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- MSELoss + Adam
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-0.8])
net[0].weight.data = torch.tensor([[-0.3]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ Adam을 사용하니 빨리 떨어지면서 잘 수렴함 (힘으로 미는 느낌)
B. 학습과정 시각화 – 가능성 있는 초기값
- MSELoss + SGD
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-3.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)- MSELoss + Adam
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-3.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ Adam을 사용하니 빨리 떨어지면서 잘 수렴함 (마지막은 살짝 돌아가는 느낌)
C. 학습과정 시각화 – 최악의 초기값
- MSELoss + SGD
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-10.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.SGD(net.parameters(),lr=0.05)
#---#
show_animation(net,loss_fn,optimizr)- MSELoss + Adam
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
net[0].bias.data = torch.tensor([-10.0])
net[0].weight.data = torch.tensor([[-1.0]])
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters(),lr=0.25)
#---#
show_animation(net,loss_fn,optimizr)🗣️ Adam을 사용하니 빨리 떨어지면서 잘 수렴함 (내려오는 힘이 강해서 그런지 마지막은 살짝 돌다가 가는 느낌)
🗣️ 현재 최적화를 잘하고 싶으면 Adam을 사용하면 됨
D. 참고자료
https://www.youtube.com/watch?v=MD2fYip6QsQ
11:50 – Momentum
12:30 – RMSprop
15:55 – Adam
🗣️ local min과 global min이 따로 있을 때
- 일반적인 경사하강법은 보통 local min에 빠짐
- Adam은 local min을 잘 탈출함 (항상은 X)
7. 로지스틱의 한계 📝
- 🗣️ Step1 관련
- x가 증가할수록 y는 1에 가까워지고
- x가 감소할수록 y가 0에 가까워지는 상황
- 또는 반대의 상황에서만 로지스틱이 잘 됨
A. 신문기사 (데이터의 모티브)
중소·지방 기업 “뽑아봤자 그만두니까”
중소기업 관계자들은 고스펙 지원자를 꺼리는 이유로 높은 퇴직률을 꼽는다. 여건이 좋은 대기업으로 이직하거나 회사를 관두는 경우가 많다는 하소연이다. 고용정보원이 지난 3일 공개한 자료에 따르면 중소기업 청년취업자 가운데 49.5%가 2년 내에 회사를 그만두는 것으로 나타났다.
중소 IT업체 관계자는 “기업 입장에서 가장 뼈아픈 게 신입사원이 그만둬서 새로 뽑는 일”이라며 “명문대 나온 스펙 좋은 지원자를 뽑아놔도 1년을 채우지 않고 그만두는 사원이 대부분이라 우리도 눈을 낮춰 사람을 뽑는다”고 말했다.
B. 가짜데이터 – 스펙의 역설
🗣️ x: 스펙, prob: 합격할 확률
df = pd.read_csv("https://raw.githubusercontent.com/guebin/DL2025/main/posts/ironyofspec.csv")
df| x | prob | y | |
|---|---|---|---|
| 0 | -1.000000 | 0.000045 | 0.0 |
| 1 | -0.998999 | 0.000046 | 0.0 |
| 2 | -0.997999 | 0.000047 | 0.0 |
| 3 | -0.996998 | 0.000047 | 0.0 |
| 4 | -0.995998 | 0.000048 | 0.0 |
| ... | ... | ... | ... |
| 1995 | 0.995998 | 0.505002 | 0.0 |
| 1996 | 0.996998 | 0.503752 | 0.0 |
| 1997 | 0.997999 | 0.502501 | 0.0 |
| 1998 | 0.998999 | 0.501251 | 1.0 |
| 1999 | 1.000000 | 0.500000 | 1.0 |
2000 rows × 3 columns
x = torch.tensor(df.x).float().reshape(-1,1)
y = torch.tensor(df.y).float().reshape(-1,1)
prob = torch.tensor(df.prob).float().reshape(-1,1)plt.plot(x,y,'o',alpha=0.02)
plt.plot(x[0],y[0],'o',label= r"observed data = $(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--b',label= r"prob (true, unknown)")
plt.legend()
🗣️ 스펙이 너무 높으면 오히려 떨어짐
C. 로지스틱으로 적합
torch.manual_seed(43052)
net = torch.nn.Sequential(
torch.nn.Linear(1,1),
torch.nn.Sigmoid()
)
loss_fn = torch.nn.BCELoss()
optimizr = torch.optim.Adam(net.parameters())
#---#
for epoc in range(5000):
## 1
yhat = net(x)
## 2
loss = loss_fn(yhat,y)
## 3
loss.backward()
## 4
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'o',alpha=0.02)
plt.plot(x[0],y[0],'o',label= r"observed data = $(x_i,y_i)$",color="C0")
plt.plot(x,prob,'--b',label= r"prob (true, unknown)")
plt.plot(x,net(x).data, '--', label= r"prob (estimated) = $(x_i,\hat{y}_i)$")
plt.legend()
- Epoch을 10억번으로 설정해도 이건 못 맞출것 같음.
- 🗣️
- 주황색 선(model)이 올라가다가 내려오는 것은 최초의 곡선이 바뀔 수 있는 범위를 벗어남 (수식적으로)
- 이런 경우 모형의 표현력이 낮다고 표현함
D. 로지스틱 한계극복 – 아이디어만
🗣️ 반반 잘라서 하면 될 것 같음
- sigmoid를 넣기 전의 상태가 직선이 아니라 꺽이는 직선이야 한다.
a = torch.nn.Sigmoid()fig,ax = plt.subplots(4,2,figsize=(8,8))
u1 = torch.tensor([-6,-4,-2,0,2,4,6])
u2 = torch.tensor([6,4,2,0,-2,-4,-6])
u3 = torch.tensor([-6,-2,2,6,2,-2,-6])
u4 = torch.tensor([-6,-2,2,6,4,2,0])
ax[0,0].plot(u1,'--o',color='C0',label = r"$u_1$")
ax[0,0].legend()
ax[0,1].plot(a(u1),'--o',color='C0',label = r"$a(u_1)=\frac{exp(u_1)}{exp(u_1)+1}$")
ax[0,1].legend()
ax[1,0].plot(u2,'--o',color='C1',label = r"$u_2$")
ax[1,0].legend()
ax[1,1].plot(a(u2),'--o',color='C1',label = r"$a(u_2)=\frac{exp(u_2)}{exp(u_2)+1}$")
ax[1,1].legend()
ax[2,0].plot(u3,'--o',color='C2', label = r"$u_3$")
ax[2,0].legend()
ax[2,1].plot(a(u3),'--o',color='C2', label = r"$a(u_3)=\frac{exp(u_3)}{exp(u_3)+1}$")
ax[2,1].legend()
ax[3,0].plot(u4,'--o',color='C3', label = r"$u_4$")
ax[3,0].legend()
ax[3,1].plot(a(u4),'--o',color='C3', label = r"$a(u_4)=\frac{exp(u_4)}{exp(u_4)+1}$")
ax[3,1].legend()
Footnotes
원래는 이렇게 썼었지.. \(y_i = w_0 + w_1x_i + \epsilon_i \quad \epsilon_i \sim {\cal N}(0,\sigma^2)\)↩︎